#include <string.h>
#include <stdlib.h>
#include "PCB.H"
#include "R2.h"
#include "MPX_SUPT.H"

//Parameters: 	name, char[] giving desired name of PCB
//				type_string, char[] giving desired type (system or application)
//				priority_string, char[] giving desired priority (-128 to 127, inclusive)
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	check_name (checks through PCB queues to see if name is already taken)
//					setep_pcb (handles dynamic allocation and initialization of PCB)
//					insert_pcb (insert the newly created PCB into the appropriate queue)
//Creates a new pcb and inserts it into the correct queue if the parameters are valid
void create_pcb(char name[], char type_string[], char priority_string[], char extra[]){

	pcb *new_pcb;
	
	if ((type_string[0] == '\0') || (priority_string[0] == '\0')) {
		printf("Not enough arguments to create\n");
		return;
	}
	
	if(extra[0] == '\0'){
		int type_int = atoi(type_string);
		int priority_int = atoi(priority_string);
		if(strlen(name) > PROC_NAME_LEN){
			printf("The name was too long\n");
		}
		else if(check_name(name) == 0){
			if(type_int == PROC_CLASS_SYS || type_int == PROC_CLASS_APP){
				if(priority_int >= -128 && priority_int <= 127){
					new_pcb = setup_PCB(name, type_int, priority_int);
					insert_pcb(new_pcb, PROC_STATE_READY, PROC_NOT_SUSP);
				}
				else{
					printf("That is not a valid priority number\n");
				}
			}
			else{
				printf("This is not a valid process class\n");
			}
		}
		else{
			printf("That name is not unique\n");
		}
	}
	else{
		printf("The create_pcb command cannot use that many argument\n");
	}
}

//Parameters: 	name, char[] giving desired name of PCB
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	remove_pcb (dequeues the selected PCB)
//					free_pcb (handles dynamic deallocation of PCB)
//Deletes a PCB by removing it from its queue and freeing its dynamically-allocated memory
void delete_pcb(char name[], char extra[]){
	if(extra[0] == '\0'){
		pcb *apcb;
		int remove_pcb_error = -1;
		int free_pcb_error = -1;
		int state;
		int susp;
		apcb = find_pcb(name);
		state = apcb->state;
		susp = apcb->suspended;
		if(apcb == NULL){
			printf("There is no pcb by that name\n");
		}
		else{
			if(state == PROC_STATE_RUNNING){
				printf("You cannot remove the current running process\n");
			}
			else if(state == PROC_STATE_READY && susp == PROC_NOT_SUSP){
				remove_pcb_error =  remove_pcb(apcb);
			}
			else if(state == PROC_STATE_READY && susp == PROC_SUSP){
				remove_pcb_error =  remove_pcb(apcb);
			}
			else if(state == PROC_STATE_BLOCKED && susp == PROC_NOT_SUSP){
				remove_pcb_error =  remove_pcb(apcb);
			}
			else if(state == PROC_STATE_BLOCKED && susp == PROC_SUSP){
				remove_pcb_error =  remove_pcb(apcb);
			}
			else{ //can never happen, just as a check though
				printf("Somehow the state was not one of out states in delete pcb\n");
			}
			if(remove_pcb_error == 0){
				free_pcb_error = free_pcb(apcb);
				if(free_pcb_error != 0){
					printf("Free PCB failed in Delete PCB\n");
				}
			}	
		}
	}
	else{
		printf("The delete command cannot use that many arguments\n");
	}
}

//Parameters: 	name, char[] giving desired name of PCB
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	remove_pcb (dequeues the selected PCB)
//					insert_pcb (insert the newly created PCB into the appropriate queue)
//Changes a PCB to the blocked state, removes it from its queue, and inserts it into the correct blocked queue
int block_pcb(char name[], char extra[]) {

	
	
	if (extra[0] == '\0') {
		//check name
		pcb *to_block;
		to_block = find_pcb(name);
		if (to_block == NULL) {
			printf("The given pcb does not exist\n");
			return 1;
		}
		else if (to_block->state == PROC_STATE_BLOCKED) {
			if (to_block->suspended == PROC_SUSP) {
				printf("The PCB is already in the suspended-blocked queue\n");
			}
			else {
				printf("The PCB is already in the blocked queue\n");
			}
			return 0;
		}
		else {
			//place in blocked state (occurs in insert)
			//insert into blocked - find based on suspended status
			if (to_block->suspended == PROC_SUSP) {
				remove_pcb(to_block);
				if (insert_pcb(to_block, PROC_STATE_BLOCKED, PROC_SUSP) != 0) {
					printf("There was an error inserting the PCB into the suspended-blocked queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the suspended-blocked queue\n");
					return 0;
				}
			}
			else {
				remove_pcb(to_block);
				if (insert_pcb(to_block, PROC_STATE_BLOCKED, PROC_NOT_SUSP) != 0) {
					printf("There was an error inserting the PCB into the blocked queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the blocked queue\n");
					return 0;
				}
			}
		}
	}
	else {
		printf("Too many arguments to block command\n");
		return 1;
	}
}

//Parameters: 	name, char[] giving desired name of PCB
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	remove_pcb (dequeues the selected PCB)
//					insert_pcb (insert the newly created PCB into the appropriate queue)
//Changes a PCB to the unblocked state, removes it from its queue, and inserts it into the correct ready queue
int unblock_pcb(char name[], char extra[]){
	pcb *to_unblock;
	if (extra[0] == '\0') {
		//check name
		to_unblock = find_pcb(name);
		if (to_unblock == NULL) {
			printf("The given pcb does not exist\n");
			return 1;
		}
		else if (to_unblock->state == PROC_STATE_READY) {
			if (to_unblock->suspended == PROC_SUSP) {
				printf("The PCB is already in the suspended-ready queue\n");
			}
			else {
				printf("The PCB is already in the ready queue\n");
			}
			return 0;
		}
		else {
			//place in ready state (occurs in insert)
			//insert into ready - find based on suspended status
			if (to_unblock->suspended == PROC_SUSP) {
				remove_pcb(to_unblock);
				if (insert_pcb(to_unblock, PROC_STATE_READY, PROC_SUSP) != 0) {
					printf("There was an error inserting the PCB into the suspended-ready queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the suspended-ready queue\n");
					return 0;
				}
			}
			else {
				remove_pcb(to_unblock);
				if (insert_pcb(to_unblock, PROC_STATE_READY, PROC_NOT_SUSP) != 0) {
					printf("There was an error inserting the PCB into the ready queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the ready queue\n");
					return 0;
				}
			}
		}
	}
	else {
		printf("Too many arguments to unblock command\n");
		return 1;
	}
}

//Parameters: 	name, char[] giving desired name of PCB
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	remove_pcb (dequeues the selected PCB)
//					insert_pcb (insert the newly created PCB into the appropriate queue)
//Changes a PCB to the suspended state, removes it from its queue, and inserts it into the correct suspended queue
int suspend_pcb(char name[], char extra[]){
	pcb *to_suspend;
	if (extra[0] == '\0') {
		//check name
		to_suspend = find_pcb(name);
		if (to_suspend == NULL) {
			printf("The given pcb does not exist\n");
			return 1;
		}
		else if (to_suspend->suspended == PROC_SUSP) {
			if (to_suspend->state == PROC_STATE_READY) {
				printf("The PCB is already in the suspended-ready queue\n");
			}
			else {
				printf("The PCB is already in the suspended-blocked queue\n");
			}
			return 0;
		}
		else {
			//place in suspended state (occurs in insert)
			//insert into suspended - find based on state
			if (to_suspend->state == PROC_STATE_READY) {
				remove_pcb(to_suspend);
				if (insert_pcb(to_suspend, PROC_STATE_READY, PROC_SUSP) != 0) {
					printf("There was an error inserting the PCB into the suspended-ready queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the suspended-ready queue\n");
					return 0;
				}
			}
			else {
				remove_pcb(to_suspend);
				if (insert_pcb(to_suspend, PROC_STATE_BLOCKED, PROC_SUSP) != 0) {
					printf("There was an error inserting the PCB into the suspended-blocked queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the suspended-blocked queue\n");
					return 0;
				}
			}
		}
	}
	else {
		printf("Too many arguments to suspend command\n");
		return 1;
	}
}

//Parameters: 	name, char[] giving desired name of PCB
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	remove_pcb (dequeues the selected PCB)
//					insert_pcb (insert the newly created PCB into the appropriate queue)
//Changes a PCB to the unsuspended state, removes it from its queue, and inserts it into the correct queue
int resume_pcb(char name[], char extra[]){
	pcb *to_resume;
	if (extra[0] == '\0') {
		//check name
		to_resume = find_pcb(name);
		if (to_resume == NULL) {
			printf("The given pcb does not exist\n");
			return 1;
		}
		else if (to_resume->suspended == PROC_NOT_SUSP) {
			if (to_resume->state == PROC_STATE_READY) {
				printf("The PCB is already in the ready queue\n");
			}
			else {
				printf("The PCB is already in the blocked queue\n");
			}
			return 0;
		}
		else {
			//place in not suspended state (occurs in insert)
			//insert into queue - find based on state
			if (to_resume->state == PROC_STATE_READY) {
				remove_pcb(to_resume);
				if (insert_pcb(to_resume, PROC_STATE_READY, PROC_NOT_SUSP) != 0) {
					printf("There was an error inserting the PCB into the ready queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the ready queue\n");
					return 0;
				}
			}
			else {
				remove_pcb(to_resume);
				if (insert_pcb(to_resume, PROC_STATE_BLOCKED, PROC_NOT_SUSP) != 0) {
					printf("There was an error inserting the PCB into the blocked queue\n");
					return 1;
				}
				else {
					printf("The PCB has been inserted into the blocked queue\n");
					return 0;
				}
			}
		}
	}
	else {
		printf("Too many arguments to suspend command\n");
		return 1;
	}
}
//Parameters: 	name, char[] giving desired name of PCB
//				priority_string, char[] giving desired priority (-128 to 127, inclusive)
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	remove_pcb (dequeues the selected PCB)
//					insert_pcb (insert the newly created PCB into the appropriate queue)
//Changes the priority of a PCB if the desired priority is valid and the PCB exists. Reinserted if necessary.
void set_priority_pcb(char name[], char priority_string[], char extra[]){
	if (extra[0] == '\0') {
		pcb *to_set;
		int new_pri, state, susp, rem_err, ins_err;
		to_set = find_pcb(name);
		new_pri = atoi(priority_string);
		if (to_set == NULL) {
			printf("The PCB with the given name does not exist\n");
		}
		else if (new_pri == to_set->priority) {
			printf("The PCB already has the given priority\n");
		}
		else {
			if ((new_pri >= -128) && (new_pri <= 127)) {
				to_set->priority = new_pri;
				state = to_set->state;
				susp = to_set->suspended;
				if (state == PROC_STATE_READY)  {
					if (susp == PROC_SUSP) {
						rem_err = remove_pcb(to_set);
						if (rem_err == 0) {
							ins_err = insert_pcb(to_set, state, susp);
							if (ins_err != 0)
								printf("There was an error inserting the PCB\n");
						}
						else {
							printf("There was an error removing the PCB\n");
						}
					}
					else {
						rem_err = remove_pcb(to_set);
						if (rem_err == 0) {
							ins_err = insert_pcb(to_set, state, susp);
							if (ins_err != 0)
								printf("There was an error inserting the PCB\n");
						}
						else {
							printf("There was an error removing the PCB\n");
						}
					}
				}
				else if (state == PROC_STATE_BLOCKED) {
					if (susp == PROC_SUSP) {
						rem_err = remove_pcb(to_set);
						if (rem_err == 0) {
							ins_err = insert_pcb(to_set, state, susp);
							if (ins_err != 0)
								printf("There was an error inserting the PCB\n");
						}
						else {
							printf("There was an error removing the PCB\n");
						}
					}
					else {
						rem_err = remove_pcb(to_set);
						if (rem_err == 0) {
							ins_err = insert_pcb(to_set, state, susp);
							if (ins_err != 0)
								printf("There was an error inserting the PCB\n");
						}
						else {
							printf("There was an error removing the PCB\n");
						}
					}
				}
				else if (state == PROC_STATE_RUNNING) {
					printf("Cannot currently change the running process' priority - future extension\n");
				}
			}
			else {
				printf("Invalid priority value\n");
			}
		}
	}
	else {
		printf("Too many arguments to set_priority command\n");
	}
}

//Parameters: 	name, char[] giving desired name of PCB
//				extra, char[] with any extra arguments
//Returns: void
//Functions called:	none
//Prints the name, state, suspended state, and priority of a PCB if it exists
void show_pcb(char name[], char extra[]){
if(extra[0] == '\0'){
	pcb *found = find_pcb(name);
	if(found == NULL){
		printf("PCB not found.\n");
	}
	else{
		//I seriously don't know if this is how this works or not :(
		printf("Name: %s \t", found->name);
		if(found->state == PROC_STATE_READY){
			printf("State: Ready ");
		}
		else if(found->state == PROC_STATE_RUNNING){
			printf("State: Running ");
		}
		else if(found->state == PROC_STATE_BLOCKED){
			printf("State: Blocked ");
		}
		if(found->suspended == PROC_NOT_SUSP){
			printf("Not Suspended \t");
		}
		else if(found->suspended == PROC_SUSP){
			printf("    Suspended \t");
		}
		printf("Priority: %d\n", found->priority);	
	}
}
else{
	printf("The show command cannot use that many arguments\n");
}
}

//Parameters: 	extra, char[] with any extra arguments
//Returns: void
//Functions called:	check_extra (determines if there are too many arguments)
//					show_all_pcbs (traverse and print all queues)
//Prints the name, state, suspended state, and priority of all PCBs in all queues
void show_all(char extra[]){
	if (check_extra(extra, "showall") == 0)
		show_all_pcbs();
}

//Parameters: 	extra, char[] with any extra arguments
//Returns: void
//Functions called:	check_extra (determines if there are too many arguments)
//					show_all_ready (traverse and print ready queue)
//Prints the name, state, suspended state, and priority of all PCBs in the ready queue
void show_ready(char extra[]){
	if (check_extra(extra, "showr") == 0)
		show_all_ready();
}

//Parameters: 	extra, char[] with any extra arguments
//Returns: void
//Functions called:	check_extra (determines if there are too many arguments)
//					show_all_blocked (traverse and print blocked queue)
//Prints the name, state, suspended state, and priority of all PCBs in the blocked queue
void show_blocked(char extra[]){
	if (check_extra(extra, "showb") == 0)
		show_all_blocked();
}

//Parameters: 	extra, char[] with any extra arguments
//Returns: void
//Functions called:	check_extra (determines if there are too many arguments)
//					show_all_suspended_ready (traverse and print suspended-ready queue)
//Prints the name, state, suspended state, and priority of all PCBs in the suspended-ready queue
void show_suspended_ready(char extra[]){
	if (check_extra(extra, "showsr") == 0)
		show_all_suspended_ready();
}

//Parameters: 	extra, char[] with any extra arguments
//Returns: void
//Functions called:	check_extra (determines if there are too many arguments)
//					show_all_suspended_blocked (traverse and print suspended-blocked queue)
//Prints the name, state, suspended state, and priority of all PCBs in the suspended-blocked queue
void show_suspended_blocked(char extra[]){
	if (check_extra(extra, "showsb") == 0)
		show_all_suspended_blocked();
}

//Parameters: 	extra, char[] with any extra arguments
//				cmd_name, char[] with the name of the calling command
//Returns: int, 0 if there are not too many arguments, 1 if there are
//Functions called:	none
//Handles generic error message printing for too many arguments
int check_extra(char extra[], char cmd_name[]) {
	if (extra[0] != NULL) {
		printf("Too many arguments to the %s command\n", cmd_name);
		return 1;
	}
	else
		return 0;
}
